package processes;

import java.util.ArrayList;
import java.util.Stack;

/**
 * Provides the class intended to store system processes.
 * @author Miroslav Vozabal.
 */
public class ProcessesRepository {
	private ProcessDescriptor parent = null;
	private ProcessDescriptor tempItem = null;
	private ProcessDescriptor tail = null;
	private Stack<ProcessDescriptor> stack; //Intended to print list inversely.
	
	/**
	 * Constructor.
	 */
	public ProcessesRepository() {
		new ArrayList<ProcessDescriptor>();
		stack = new Stack<ProcessDescriptor>();
	}

	/**
	 * Inverse the process list.
	 * @param processList
	 * @return reference of inversed list
	 */
	private ArrayList<ProcessDescriptor> inverseList(ArrayList<ProcessDescriptor> processList) {
		stack = new Stack<ProcessDescriptor>();
		ArrayList<ProcessDescriptor> inversedList = new ArrayList<ProcessDescriptor>();
		
		for (ProcessDescriptor item : processList) {
			stack.push(item);
		}
		while(stack.isEmpty() == false) {
			inversedList.add(stack.pop());
		}
		return inversedList;	
	}
	
	/**
	 * Adds items from ArrayList to the ProcessesRepository.  Usually the first item from ArrayList is used like parent for its children.   
	 * @param list
	 */
	public void addItems(ArrayList<ProcessDescriptor> list) {		
		list = inverseList(list);
		int i = 0;
		if (tail == null) {			
			for (ProcessDescriptor processDescriptor : list) {			
				if (i == 0) {
					tail = processDescriptor;
				}
				else {					
					tempItem = tail;
					tail = processDescriptor;
					tail.setSibling(tempItem);
				}
				i++;
			}
			i = 0;
		}
		else {
			for (ProcessDescriptor processDescriptor : list) {
				String parentWorkingDirectory = tail.getWorkingDirectory();
				if (i == 0) { //first item
					parent = tail;
					tempItem = tail;
					tail = processDescriptor;
					tail.setParent(tempItem);
					parent.addChild(tail);		
				}
				else {
					tempItem = tail; //next items
					tail = processDescriptor;
					tail.setSibling(tempItem);
					parent.addChild(tail);
				}
				if(tail.getWorkingDirectory() == "" || tail.getWorkingDirectory() == null){
					tail.setWorkingDirectory(parentWorkingDirectory);
				}
				i++;
			}
			i = 0;
		}
	}
	
	/**
	 * Prints list items from tail.
	 */
	public void printList() {
		ProcessDescriptor item;		
		item = tail;
		
		while (true) {
			if (item.getParent() == null && item.getSibling() == null) {
				break;								
			}
			else if (item.getParent() != null && item.getSibling() == null) {
				item = item.getParent();
			}
			else if (item.getParent() == null && item.getSibling() != null) {
					item = item.getSibling();
			}		
		}
	}
	
	/**
	 * Prints list items inversely from tail.
	 * @return
	 */
	public String printListInversely() {
		String result = new String();
		stack = new Stack<ProcessDescriptor>();
		ProcessDescriptor item;
		if (tail == null) {
			result = "There are no processes!\n";
		}
		else {			
			item = tail;
			
			while (true) {
				stack.add(item);
				
				if (item.getParent() == null && item.getSibling() == null) {
					break;								
				}
				else if (item.getParent() != null && item.getSibling() == null) {
					item = item.getParent();
				}
				else if (item.getParent() == null && item.getSibling() != null) {
						item = item.getSibling();
				}		
			}

			result += "PID CMD\n";
			while (!stack.isEmpty()) {
				ProcessDescriptor temp = stack.pop();
				result += "  " + temp.getId() + " " + temp.getApplicationName() + "\n";		
			}
		}
		return result;
	}
	/**
	 * Finds and returns item.
	 * @param itemID 
	 * @return item with specified id.
	 */
	public ProcessDescriptor findItem(int itemID) {
		ProcessDescriptor item;		
		item = tail;
		while (true) {
			if (item.getId() == itemID) {
				//Found
				break;
			}
			else {
				//Searching
				if (item.getParent() == null && item.getSibling() == null) {
					item = null;
					break;								
				}
				else if (item.getParent() != null && item.getSibling() == null) {
					item = item.getParent();
				}
				else if (item.getParent() == null && item.getSibling() != null) {
						item = item.getSibling();
				}
			}		
		}
		return item;
	}
	
	/**
	 * Gets children on the specific position.
	 * @param itemId
	 * @return
	 */
	public ArrayList<ProcessDescriptor> getChildren(int itemId) {
		ProcessDescriptor item = findItem(itemId);
		return item.getChildren();	
	}
	/**
	 * Returns the tail of ProcessesRepository.
	 * @return
	 */
	public ProcessDescriptor getTail() {
		return tail;
	}
	
	/**
	 * Cleans item from the list. Tail moves to a brother or the parent position.
	 */
	public void cleanTail() {
		ProcessDescriptor parrent;
		ProcessDescriptor temp;
		temp = tail;
		parrent = getParent(tail);
		if (tail.getSibling() == null) {
			tail = parrent;
			if (parrent != null) {
				parrent.cleanChild(temp);				
				temp.setParent(null);
			}
		}
		else {
			tail = temp.getSibling();
			parrent.cleanChild(temp);
			
		}
	}
	
	/**
	 * Returns parent of procesDeskriptor.
	 * @param processDescriptor
	 * @return
	 */
	public ProcessDescriptor getParent(ProcessDescriptor processDescriptor) {
		ProcessDescriptor pd = processDescriptor;
		while (pd.getSibling() != null) {
			pd = pd.getSibling();
		}
		if (pd.getParent() != null) {
			pd = pd.getParent();
			return pd;
		}
		else {
			return null;
		}
	}
	/**
	 * Cleans the item from the list.
	 * @param processDescriptor
	 */
	public void cleanItem(ProcessDescriptor processDescriptor) {
		tail = processDescriptor;
		cleanTail();		
	}
	
}
